home *** CD-ROM | disk | FTP | other *** search
Wrap
Text File | 1989-01-20 | 14.5 KB | 633 lines | [ TEXT/MPS ]
MODULE FixPObj; (* Go through an object code file and change dictionary occurrences of one string to another string. The default behavior is to change "QUICKDRAW" to "QuickDraw__Globals" which makes code generated by the Pascal compiler compatible with code generated by the Modula compiler. This utility only understands MPW object file format version 1. It will print a warning if it encounters a later format version. Arguments: --Input file name is required. --Output file name may be specified with '-o' option, otherwise a default output name of FixP.o will be used. The output file name must be different from the input file name. --Strings for substitution may be specified with the '-s' option. i.e., "-s QUICKDRAW=QuickDraw__Globals" would specify the default behavior. Written by John N. Calley 9/20/88 *) FROM Diagnostic IMPORT (*procs*) WriteString, WriteCard, WriteLn, WriteLongInt, WriteInt; FROM FileManager IMPORT (*types*) FInfo, (*procs*) GetFInfo, SetFInfo; FROM IntEnv IMPORT (*vars *) ArgC, ArgV, Exit; FROM IntEnvIO IMPORT (*const*) InputFD, OutputFD, RDONLY, WRONLY, CREAT, TRUNC, (*procs*) ErrNo, open, read, write, close; FROM MacTypes IMPORT (*types*) Str255, StringHandle, OSErr; FROM MemoryManager IMPORT (*procs*) NewHandle, DisposHandle, HLock, HUnlock; FROM Strings IMPORT (*procs*) Length, MakePascalString, Copy, Pos; FROM SYSTEM IMPORT (*types*) ADDRESS, (*procs*) VAL, SHIFT, ADR, LONG; FROM Utilities IMPORT (*procs*) Munger; CONST defaultOutFile = "FixP.o"; pp = FALSE; (* print progress information *) VAR inFileName, outFileName, inString, outString :Str255; inFile, (* File IDs for input and output files *) outFile, status (* Status of last read or write operation *) :LONGINT; PROCEDURE PrintUsage(); (* Print usage statement. *) BEGIN WriteString ("# "); WriteString (ArgV^[0]^); WriteString (": Bad option or unable to open file."); WriteLn(); WriteString ("# Usage: "); WriteString (ArgV^[0]^); WriteString (" [-s oldString=newString] [-o outFileName] inFileName"); WriteLn(); END PrintUsage; PROCEDURE SetOptions():BOOLEAN; (* Set up input file name, optional output file name and optional string substitutions. Return TRUE if all options are interpretable, FALSE if there is an unrecognizable option or if no input file name is given. *) VAR i, j :INTEGER; tempLength :INTEGER; optionsOK :BOOLEAN; equalPos (* Position of '=' in substitution option *) :INTEGER; BEGIN (* Set defaults *) inFileName := ""; outFileName := defaultOutFile; inString := "QUICKDRAW"; (* default substitutions *) outString := "QuickDraw__Globals"; optionsOK := TRUE; i := 1; WHILE i < ArgC DO IF ArgV^[i]^[0] = '-' THEN IF CAP(ArgV^[i]^[1]) = "o" THEN INC(i); (* next argument should be output file name *) outFileName := VAL(Str255, ArgV^[i]^); ELSIF CAP(ArgV^[i]^[1]) = "s" THEN INC(i); (* next argument shoud be set of substitution strings *) equalPos := Pos ("=", ArgV^[i]^); IF equalPos = -1 THEN optionsOK := FALSE; ELSE Copy (ArgV^[i]^, 0, equalPos, inString); Copy (ArgV^[i]^, equalPos + 1, VAL(INTEGER, Length(ArgV^[i]^)) - equalPos, outString); END; (*IF*) ELSE (* Unknown '-' option *) optionsOK := FALSE; END; (*IF*) ELSE (* We assume it is the input file name *) inFileName := VAL(Str255, ArgV^[i]^); END; (*IF*) INC(i); END; (*WHILE*) RETURN (optionsOK); END SetOptions; PROCEDURE OpenFiles():BOOLEAN; (* Open the files indicated by <inFileName> and <outFileName>. Return TRUE if the operations are successful, FALSE otherwise. *) VAR success :BOOLEAN; BEGIN success := TRUE; IF Length(inFileName) = 0 THEN success := FALSE; ELSE inFile := open (inFileName, RDONLY); IF ErrNo() <> 0D THEN success := FALSE; END; (*IF*) END; (*IF*) IF Length(outFileName) = 0 THEN outFile := OutputFD; (* Standard output *) ELSE outFile := open (outFileName, WRONLY + CREAT + TRUNC); IF ErrNo() <> 0D THEN success := FALSE; END; (*IF*) END; (*IF*) RETURN (success); END OpenFiles; PROCEDURE ReadWord (VAR value:CARDINAL); (* Read the next two characters as a binary value and return that value. *) BEGIN status := read (inFile, ADR(value), 2D); END ReadWord; PROCEDURE WriteWord (value:CARDINAL); (* Write out the indicated integer as two consecutive bytes. *) BEGIN status := write (outFile, ADR(value), 2D); END WriteWord; PROCEDURE WriteByte (value:INTEGER); (* Write out the low byte of the indicated integer. *) BEGIN status := write (outFile, ADR(value) + 1D, 1D); END WriteByte; PROCEDURE ReadByte ():CHAR; (* Read in one byte and return it as a character *) VAR tempChar :CHAR; BEGIN status := read (inFile, ADR(tempChar), 1D); RETURN (tempChar); END ReadByte; PROCEDURE Pass (length:CARDINAL); (* Pass through the indicated number of bytes from input to output. *) VAR tempStr :Str255; tempLength :CARDINAL; BEGIN WHILE length > 0 DO IF length > 256 THEN tempLength := 256; DEC(length, 256); ELSE tempLength := length; length := 0; END; (*IF*) status := read (inFile, ADR(tempStr), LONG(tempLength)); status := write (outFile, ADR(tempStr), LONG(tempLength)); END; (*WHILE*) END Pass; PROCEDURE ReadString (VAR string:Str255; VAR length:INTEGER); (* Read the pascal formatted string from the input and return it in <string>. <length> is the length of the returned string. *) VAR inChar :CHAR; BEGIN status := read (inFile, ADR(inChar), 1D); length := VAL(INTEGER, inChar); status := read (inFile, ADR(string), LONG(length)); string[length] := VAL(CHAR, 0); (* null terminate the string *) END ReadString; PROCEDURE WritePString (string:Str255); (* Write out the indicated string in pascal format. *) VAR tempStr :Str255; BEGIN MakePascalString (string, tempStr); status := write (outFile, ADR(tempStr), LONG(VAL(INTEGER, tempStr[0]) + 1)); END WritePString; PROCEDURE ProcessFirst(); (* Pass a First record through. Print a warning if the version number is later that the latest we know about (1). *) VAR version :CARDINAL; BEGIN IF pp THEN WriteString ("First"); WriteLn(); END; (*IF*) WriteByte (1); Pass (1); ReadWord (version); WriteWord (version); IF version > 1 THEN WriteString ("# Warning: Unknown object file format version. "); WriteLn(); WriteString ("# Output may not be correct."); WriteLn(); END; (*IF*) END ProcessFirst; PROCEDURE ProcessLast(); (* Pass a Last record through. *) BEGIN IF pp THEN WriteString ("Last"); WriteLn(); END; (*IF*) WriteByte (2); Pass (1); END ProcessLast; PROCEDURE ProcessComment(); (* Pass a comment record on through. *) VAR size :CARDINAL; BEGIN IF pp THEN WriteString ("Comment record"); WriteLn(); END; (*IF*) WriteByte (3); Pass (1); ReadWord (size); WriteWord (size); Pass (size - 4); END ProcessComment; PROCEDURE ReadDict (dict:StringHandle; length:LONGINT); (* Read <length> bytes from standard input into the handle <dict>. *) BEGIN HLock (dict); status := read (inFile, dict^, length); HUnlock (dict); END ReadDict; PROCEDURE ModifyDict (dict:StringHandle):BOOLEAN; (* Substitute <outString> for the string <inString> in <dict>. There will not be more that one occurrence. Return TRUE if a replacement was done, FALSE if no replacement occurred. *) VAR pInString, (* <inString> and <outString> are modula strings, we actually need *) pOutString (* to replace pascal format strings. *) :Str255; result :LONGINT; BEGIN MakePascalString (inString, pInString); MakePascalString (outString, pOutString); result := Munger (dict, 2, ADR(pInString), LONG(VAL(INTEGER, pInString[0]) + 1), ADR(pOutString), LONG(VAL(INTEGER, pOutString[0]) + 1)); IF result > 0D THEN RETURN(TRUE); ELSE RETURN(FALSE); END; (*IF*) END ModifyDict; PROCEDURE WriteDict (dict:StringHandle; length:LONGINT); (* Write <length> bytes from <dict> to standard output. *) BEGIN HLock (dict); status := write (outFile, dict^, length); HUnlock (dict); END WriteDict; PROCEDURE ProcessDict(); (* Process a dictionary record. If the record defines the string <inString> then replace it with the string <outString> and write out the modified dictionary record. If it does not contain <inString> write it out unchanged. Method: Find out what the current length of the record is. Allocate a handle that is large enough for the record after the string has been changed. Read the record into the handle. Use Munger to perform the substitution if any. Write the potentially modified record back out. *) VAR inChar :CHAR; length (* length of the dictionary record *) :CARDINAL; wasOdd (* TRUE if the original dictionary record had an odd length *) :BOOLEAN; dict :StringHandle; BEGIN IF pp THEN WriteString ("Dictionary"); WriteLn(); END; (*IF*) inChar := ReadByte(); (* This byte should always be 0 *) ReadWord (length); (* length of the dictionary record *) IF pp THEN WriteString ("Dictionary length is "); WriteCard (length, 4); WriteLn(); END; (*IF*) wasOdd := ODD(length); dict := NewHandle (length + Length(outString)); length := length - 4; (* Compensate for the fact that we have already read 4 bytes of header*) ReadDict (dict, length); (* Read the dictionary into the <dict> handle *) IF ModifyDict (dict) THEN IF pp THEN WriteString ("Changed: Old length = "); WriteCard (length, 4); END; (*IF*) length := length + Length(outString) - Length(inString); IF pp THEN WriteString (" outString = '"); WriteString (outString); WriteString ("' length = '"); WriteCard (Length(outString), 4); WriteString (" inString = '"); WriteString (inString); WriteString ("' length = '"); WriteCard (Length(inString), 4); WriteLn(); WriteString (" New Dictionary length = "); WriteCard (length, 4); WriteLn(); END; (*IF*) END; WriteByte (4); WriteByte (0); WriteWord (length + 4); WriteDict (dict, length); IF NOT (wasOdd = ODD(length)) THEN WriteByte (0); (* Write a pad record *) END; (*IF*) DisposHandle (dict); END ProcessDict; PROCEDURE ProcessPad(); (* Acknowledge that a padding record has been read. *) BEGIN WriteByte (0); IF pp THEN WriteString ("Pad"); WriteLn(); END; (*IF*) END ProcessPad; PROCEDURE ProcessDataModule(); (* Pass a data module record on through. *) VAR moduleID, size :CARDINAL; BEGIN ReadWord (moduleID); WriteWord (moduleID); ReadWord (size); WriteWord (size); IF pp THEN WriteString ("Data Module: "); WriteCard (moduleID, 4); WriteString ("size is "); WriteCard (size, 4); WriteLn(); END; (*IF*) END ProcessDataModule; PROCEDURE ProcessCodeModule(); (* Pass a code module record on through. *) VAR moduleID, segID :CARDINAL; BEGIN ReadWord (moduleID); WriteWord (moduleID); ReadWord (segID); WriteWord (segID); IF pp THEN WriteString ("Code Module: "); WriteCard (moduleID, 4); WriteString (" seg ID:"); WriteCard (segID, 4); WriteLn(); END; (*IF*) END ProcessCodeModule; PROCEDURE ProcessModule(); (* Pass a module record on through. *) VAR inChar :CHAR; flags :INTEGER; BEGIN WriteByte (5); inChar := ReadByte (); (*flags*) flags := VAL(INTEGER, inChar); WriteByte (flags); IF ODD(flags) THEN ProcessDataModule(); ELSE ProcessCodeModule(); END; (*IF*) END ProcessModule; PROCEDURE ProcessEntryPoint(); (* Pass an entry point record on through. *) BEGIN WriteByte (6); Pass (7); IF pp THEN WriteString ("Entry Point"); WriteLn(); END; (*IF*) END ProcessEntryPoint; PROCEDURE ProcessSize(); (* Pass a size record on through. *) BEGIN WriteByte (7); Pass (5); END ProcessSize; PROCEDURE ProcessContents(); (* Pass a contents record on through. *) VAR size (* Size of the contents record *) :CARDINAL; BEGIN WriteByte (8); Pass (1); (* flags *) ReadWord (size); WriteWord (size); IF pp THEN WriteString ("Contents: size="); WriteCard (size, 4); WriteLn(); END; (*IF*) Pass (size - 4); END ProcessContents; PROCEDURE ProcessReference (); (* Pass a reference record on through. *) VAR size :CARDINAL; BEGIN WriteByte (9); IF pp THEN WriteString ("Reference Record"); WriteLn(); END; (*IF*) Pass (1); (* flags *) ReadWord (size); WriteWord (size); Pass (size - 4); END ProcessReference; PROCEDURE ProcessCReference (); (* Pass a computed reference record on through. *) VAR size :CARDINAL; BEGIN WriteByte (10); IF pp THEN WriteString ("Computed Reference Record"); WriteLn(); END; (*IF*) Pass (1); (* flags *) ReadWord (size); WriteWord (size); Pass (size - 4); END ProcessCReference; PROCEDURE Dispatch (inChar:CHAR); (* Decide who should process this and dispatch control to them. *) BEGIN CASE VAL(INTEGER, inChar) OF 0 : ProcessPad(); | 1 : ProcessFirst(); | 2 : ProcessLast(); | 3 : ProcessComment(); | 4 : ProcessDict(); | 5 : ProcessModule(); | 6 : ProcessEntryPoint(); | 7 : ProcessSize(); | 8 : ProcessContents(); | 9 : ProcessReference(); | 10: ProcessCReference(); | ELSE (* This happens when the byte past the last byte of the file is read. Ignore it. *) END; (*CASE*) END Dispatch; PROCEDURE SetOutFileType(); (* We created a text file, we need to make it into and OBJ file so that the linker will accept it. *) VAR fInfo :FInfo; err :INTEGER; BEGIN err := GetFInfo (outFileName, 0, fInfo); IF err = 0 THEN fInfo.fdType := 'OBJ '; err := SetFInfo (outFileName, 0, fInfo); END; (*IF*) IF err <> 0 THEN WriteString ("# Problem setting output file type to 'OBJ '"); WriteLn(); END; (*IF*) END SetOutFileType; BEGIN (*Main*) IF SetOptions() AND OpenFiles() THEN REPEAT Dispatch (ReadByte()); UNTIL status = 0D; SetOutFileType(); Exit (0D); ELSE PrintUsage(); Exit (1D); END; (*IF*) END FixPObj. ove it back so that it points at the beginning of the record instead of to the end. We can then use this pointer to directly set and look at the Pascal data. Accessing the data from a Modula program presents a somewhat different problem. Modula, like Pascal, expects global data to be accessed as offsets into a data module. Unlike Pascal, however, Modula does not read the original source code in order to find a particular piece of data.